package com.wyfan.scaffold.common.config.redis;

import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.data.redis.connection.*;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import java.time.Duration;
import java.util.ArrayList;
import java.util.List;

/**
 * redis 公共方法
 */
class RedisFactory {

    /* ------------------------------------------------公共方法------------------------------------------------------------*/
    /**
     * 创建实例
     */
    static StringRedisTemplate createStringRedisTemplate(RedisProperties prop) {
        if (prop == null || StringUtils.isEmpty(prop.getHost())) return null;
        LettuceConnectionFactory factory = getRedisTemplate(prop);
        return createRedisTemplateString(factory);
    }

    /**
     * 创建zlib压缩存储实例
     */
    static StringRedisTemplate createStringRedisTemplateZlib(RedisProperties prop) {
        if (prop == null || StringUtils.isEmpty(prop.getHost())) return null;
        LettuceConnectionFactory factory = getRedisTemplate(prop);
        return createRedisTemplateStringZlib(factory);
    }

    /**
     * ConnectionFactory
     */
    private static LettuceConnectionFactory getRedisTemplate(RedisProperties prop) {

        RedisConfiguration config;
        if (!StringUtils.isEmpty(prop.getNodes())) {
            /* ========= 集群基本配置 ========= */
            RedisClusterConfiguration configuration = new RedisClusterConfiguration();
            List<RedisNode> nodeList = getRedisNodes(prop.getNodes());
            configuration.setClusterNodes(nodeList);
            if (!ObjectUtils.isEmpty(prop.getPassword())) {
                RedisPassword redisPassword = RedisPassword.of(prop.getPassword());
                configuration.setPassword(redisPassword);
            }
            config = configuration;
        } else if (!StringUtils.isEmpty(prop.getSentinelNodes()) ) {
            /* ========= 哨兵基本配置 ========= */
            RedisSentinelConfiguration configuration = new RedisSentinelConfiguration();
            List<RedisNode> nodeList = getRedisNodes(prop.getSentinelNodes());
            configuration.setSentinels(nodeList);
            configuration.setMaster(prop.getSentinelMaster());
            if (!ObjectUtils.isEmpty(prop.getPassword())) {
                RedisPassword redisPassword = RedisPassword.of(prop.getPassword());
                configuration.setPassword(redisPassword);
            }
            config = configuration;
        } else {
            /* ========= 单机基本配置 ========= */
            RedisStandaloneConfiguration configuration = new RedisStandaloneConfiguration();
            configuration.setHostName(prop.getHost());
            configuration.setPort(prop.getPort());
            configuration.setDatabase(prop.getDatabase());
            if (!ObjectUtils.isEmpty(prop.getPassword())) {
                RedisPassword redisPassword = RedisPassword.of(prop.getPassword());
                configuration.setPassword(redisPassword);
            }
            config = configuration;
        }




        /* ========= 连接池通用配置 ========= */
        GenericObjectPoolConfig genericObjectPoolConfig = new GenericObjectPoolConfig();
        genericObjectPoolConfig.setMaxTotal(prop.getMaxActive());
        genericObjectPoolConfig.setMinIdle(prop.getMinIdle());
        genericObjectPoolConfig.setMaxIdle(prop.getMaxIdle());
        genericObjectPoolConfig.setMaxWaitMillis(prop.getMaxWait());
        genericObjectPoolConfig.setTestOnBorrow(true);

        /* ========= lettuce pool ========= */
        LettucePoolingClientConfiguration.LettucePoolingClientConfigurationBuilder builder = LettucePoolingClientConfiguration.builder();
        builder.poolConfig(genericObjectPoolConfig);
        builder.commandTimeout(Duration.ofSeconds(prop.getTimeout()));
        LettuceConnectionFactory connectionFactory = new LettuceConnectionFactory(config, builder.build());
        connectionFactory.afterPropertiesSet();


        return connectionFactory;
    }

    private static List<RedisNode> getRedisNodes(String nodeConfigStr) {
        List<RedisNode> nodeList = new ArrayList<>();
        String[] nodeArray = nodeConfigStr.split(",");
        for (String nodeStr : nodeArray) {
            String h = nodeStr.split(":")[0];
            int p = Integer.parseInt(nodeStr.split(":")[1]);
            nodeList.add(new RedisNode(h, p));
        }
        return nodeList;
    }


    /**
     * key value， String序列化
     */
    private static StringRedisTemplate createRedisTemplateString(RedisConnectionFactory redisConnectionFactory) {
        StringRedisTemplate redisTemplate = new StringRedisTemplate();
        redisTemplate.setConnectionFactory(redisConnectionFactory);

        RedisSerializer stringSerializer = new StringRedisSerializer();
        redisTemplate.setValueSerializer(stringSerializer);
        redisTemplate.setKeySerializer(stringSerializer);
        redisTemplate.setHashValueSerializer(stringSerializer);
        redisTemplate.setHashKeySerializer(stringSerializer);
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }

    /**
     * 自定义序列化,字符串压缩存储
     */
    private static StringRedisTemplate createRedisTemplateStringZlib(RedisConnectionFactory redisConnectionFactory) {
        StringRedisTemplate redisTemplate = new StringRedisTemplate();
        redisTemplate.setConnectionFactory(redisConnectionFactory);

        RedisSerializer stringSerializer = new StringRedisSerializer();
        redisTemplate.setKeySerializer(stringSerializer);
        redisTemplate.setValueSerializer(new ZlibRedisSerializer());
        redisTemplate.setHashKeySerializer(stringSerializer);
        redisTemplate.setHashValueSerializer(new ZlibRedisSerializer());
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }
}
